/**
 * 
 */
package edu.umd.clip.lm.model.data;

import java.io.IOException;
import java.util.*;

/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class ReadableTrainingData extends AbstractTrainingData {
	protected final TrainingDataReader reader;
	private boolean finished = false;
	private Queue<TrainingDataBlock> buffer;
	private boolean started = false;
	private long bufferLimit;
	private long bufferSize;
	public final static long DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024;
	
	public ReadableTrainingData(TrainingDataReader reader) {
		this(reader, DEFAULT_BUFFER_SIZE);
	}
	
	public ReadableTrainingData(TrainingDataReader reader, long bufferSize) {
		this.reader = reader;
		this.bufferLimit = bufferSize;
		this.buffer = new LinkedList<TrainingDataBlock>();
		this.bufferSize = 0;
	}
	
	public void start() {
		if (!started) {
			reader.start(this);
			started = true;
		}
	}
	
	public boolean hasNext() {
		if (!started) {
			start();
		}
		
		if (!buffer.isEmpty()) {
			return true;
		}
		
		if (finished) return false;
		
		fillBuffer();
		
		return !buffer.isEmpty();
	}

	private synchronized void fillBuffer() {
		if (finished) return;

		try {
			while(bufferSize < bufferLimit) {
				TrainingDataBlock block = reader.getNextBlock();
				if (block == null || block.isTerminator()) {
					finished = true;
					reader.finishedReadingData();
					break;
				}
				bufferSize += block.getByteSize();
				buffer.offer(block);
			}
		} catch(IOException e) {
			
		}
	}
	
	public TrainingDataBlock peek() throws IOException {
		if (hasNext()) {
			return buffer.peek();
		}
		return null;
	}
	
	public TrainingDataBlock next() throws IOException {
		if (hasNext()) {
			TrainingDataBlock block = buffer.poll();
			if (block != null) {
				bufferSize -= block.getByteSize();
			}
			return block;
		}
		return null;
	}
	
	/*
	public boolean canReset() {
		return reader.canReset();
	}
	public void reset() throws IOException {
		reader.reset();
		finished = false;
		buffer.clear();
		bufferSize = 0;
	}
	*/
	
	void parseBuffer() {
		for(TrainingDataBlock block : buffer) {
			block.parse();
		}
	}

	long getBufferSize() {
		return bufferSize;
	}
	
	public boolean isFinished() {
		return finished;
	}
}
